package com.yaolong.api.task;

import com.yaolong.api.modules.gen.utils.MybatisConfigurationUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import java.nio.file.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author yaolong
 * @version V1.0.0
 * @program opms
 * @description mapper文件刷新任务
 * @create 2023-01-30 11:12
 **/

@Slf4j
public class MapperHotRefreshTask implements Runnable {
    private final Configuration configuration;


    private final Path watchDir;


    public MapperHotRefreshTask(Configuration configuration, Path watchDir) {
        this.configuration = configuration;
        this.watchDir = watchDir;
    }

    @SneakyThrows
    @Override
    public void run() {
        WatchService watcher = FileSystems.getDefault().newWatchService();
        Set<Path> paths = MybatisConfigurationUtil.findFile(watchDir, x -> x.toFile().getName().endsWith(".xml"));
        HashMap<String, Path> watchFiles = new HashMap<>();
        int size = paths.size();
        for (Path path : paths) {
            watchFiles.put(path.toFile().getName(), path);
            log.info(String.format("watch xml file [%s] >>>> %s", size--, path));
        }
        Set<Path> watchPaths = watchFiles.values().stream().map(Path::getParent).collect(Collectors.toSet());
        for (Path watchPath : watchPaths) {
            watchPath.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
        }

        while (true) {
            try {
                WatchKey watchKey = watcher.take();
                Set<Path> changeFiles = new HashSet<>();
                List<WatchEvent<?>> watchEvents = watchKey.pollEvents();
                for (WatchEvent<?> event : watchEvents) {
                    if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
                        continue;
                    }
                    Object context = event.context();
                    if (context instanceof Path) {
                        Path pathFile = (Path) context;
                        if (pathFile.toFile().getName().endsWith(".xml")) {
                            changeFiles.add(watchFiles.get(pathFile.toFile().getName()));
                        }
                    }
                }
                // 重新加载xml
                reloadXml(changeFiles);
                boolean valid = watchKey.reset();
                if (!valid) {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("=================== mybatis-refresh error exit ======================");
            }
        }


    }

    /**
     * 更新xml文件
     *
     * @param changeFiles 变化的xml文件
     */
    @SneakyThrows
    private synchronized void reloadXml(Set<Path> changeFiles) {
        for (Path path : changeFiles) {
            Resource resource = new FileSystemResource(path);
            log.info("========== HOT REFRESH XML : {}", path);
            Thread.sleep(1000);
            try {
                String namespace = MybatisConfigurationUtil.getNamespace(resource);
                MybatisConfigurationUtil.clearMap(configuration, namespace);
                MybatisConfigurationUtil.clearSet(configuration, resource);
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments());
                xmlMapperBuilder.parse();
                log.info("========== HOT REFRESH XML SUCCESS");
            } catch (Exception e) {
                log.info("========== HOT REFRESH XML FAILED : " + path);
                e.printStackTrace();
            } finally {
                ErrorContext.instance().reset();
            }
        }
    }
}
